package com.liting.uitest.imgutils;

import com.liting.uitest.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * 视觉强度多线程
 */
@Service
@Slf4j
public class VisionExecutor {
    @Async("asyncExecutor")
    public CompletableFuture<VisionIntentSquareAggreGation> createSquareInner(double minX, double minY, int beginCol, int endCol, int beginRow, int endRow,
                                                                              double imgCenterX, double imgCenterY, BigDecimal[] rList,
                                                                              BigDecimal sideLength
    ) {
        Map<VisionIntentSquare, Integer> squreGradeMap = new HashMap<>();
        HashSet<BigDecimal> centerXSet = new HashSet<>();
        HashSet<BigDecimal> centerYSet = new HashSet<>();

        for (int i = beginCol; i < endCol; i++) {
            double tempX = minX + i * (sideLength.doubleValue());
            BigDecimal decimalX = new BigDecimal(tempX).setScale(6, RoundingMode.UP);
            centerXSet.add(decimalX);

            for (int j = beginRow; j < endRow; j++) {
                double tempY = minY + j * (sideLength.doubleValue());
                BigDecimal decimalY = new BigDecimal(tempY).setScale(6, RoundingMode.UP);
                centerYSet.add(decimalY);

                double distance = twoPointLen(tempX, tempY, imgCenterX, imgCenterY);

                int grade = getGrade(distance, rList);

                VisionIntentSquare square = new VisionIntentSquare(
                        decimalX, decimalY, grade, sideLength);

                squreGradeMap.put(square, new Integer(grade));


            }
        }
        VisionIntentSquareAggreGation aggreGation = new VisionIntentSquareAggreGation();
        aggreGation.setSqureGradeMap(squreGradeMap);
        aggreGation.setCenterXSet(centerXSet);
        aggreGation.setCenterYSet(centerYSet);

        return CompletableFuture.completedFuture(aggreGation);
    }

    @Async("asyncExecutor")
    public CompletableFuture<Map<String, Map<Integer, Integer>>> createRectGradeSquareMap(Rectangle rectangle, VisionIntentSquareAggreGation squareAggreGation) {

        int[] minMaxXY = findMinMaxXY(rectangle, squareAggreGation);


        int beginCenterX = minMaxXY[0], beginCenterY = minMaxXY[1], endCenterX = minMaxXY[2], endCenterY = minMaxXY[3];


        Map<VisionIntentSquare, Integer> squreGradeMap = squareAggreGation.getSqureGradeMap();
        Map<Integer, Integer> result = new HashMap<>();

        for (int i = beginCenterX; i <= endCenterX; i++) {
            for (int j = beginCenterY; j <= endCenterY; j++) {
                VisionIntentSquare temp = new VisionIntentSquare(
                        squareAggreGation.getCenterXList().get(i),
                        squareAggreGation.getCenterYList().get(j),
                        0, new BigDecimal(squareAggreGation.getSquareSide()));
                Integer grade = squreGradeMap.get(temp);

                Integer count = result.get(grade);
                if (count == null) {
                    count = 0;
                }
                count++;
                result.put(grade, count);
            }
        }
        Map<String, Map<Integer, Integer>> retureInfo = new HashMap<>();
        retureInfo.put(rectangle.getName(), result);

        return CompletableFuture.completedFuture(retureInfo);

    }

    /**
     * 每个圆形包括的各个级别的正方形的数量
     *
     * @param circle            圆形信息
     * @param squareAggreGation 正方形的所有信息
     * @return Map的key是级别，value是正方形数量
     */
    @Async("asyncExecutor")
    public CompletableFuture<Map<String, Map<Integer, Integer>>> createCircleradeSquareMap(Circle circle, VisionIntentSquareAggreGation squareAggreGation) {
        //1. 取得圆形的外切矩形
        Rectangle rectangle = new Rectangle();
        rectangle.setHeight(circle.getRadius() * 2);
        rectangle.setWidth(circle.getRadius() * 2);
        rectangle.setLeftTopX(circle.getCenterX() - circle.getRadius());
        rectangle.setLeftTopY(circle.getCenterY() - circle.getRadius());

        //2. 如同createRectGradeSquareMap，获得beginCenterX,beginCenterY,endCenterX,endCenterY
        int[] minMaxXY = findMinMaxXY(rectangle, squareAggreGation);
        int beginCenterX = minMaxXY[0], beginCenterY = minMaxXY[1], endCenterX = minMaxXY[2], endCenterY = minMaxXY[3];

        //3. 循环，判断每个中旬到圆心的距离是否小于圆的半径，小于计算，大于不包括
        Map<VisionIntentSquare, Integer> squreGradeMap = squareAggreGation.getSqureGradeMap();
        Map<Integer, Integer> result = new HashMap<>();

        for (int i = beginCenterX; i <= endCenterX; i++) {
            for (int j = beginCenterY; j <= endCenterY; j++) {
                double distance = twoPointLen(
                        squareAggreGation.getCenterXList().get(i).doubleValue(),
                        squareAggreGation.getCenterYList().get(j).doubleValue(),
                        circle.getCenterX(), circle.getCenterY());
                if (distance < circle.getRadius()) {
                    VisionIntentSquare temp = new VisionIntentSquare(
                            squareAggreGation.getCenterXList().get(i),
                            squareAggreGation.getCenterYList().get(j),
                            0, new BigDecimal(squareAggreGation.getSquareSide()));
                    Integer grade = squreGradeMap.get(temp);

                    Integer count = result.get(grade);
                    if (count == null) {
                        count = 0;
                    }
                    count++;
                    result.put(grade, count);
                }

            }
        }

        Map<String, Map<Integer, Integer>> retureInfo = new HashMap<>();
        retureInfo.put(circle.getName(), result);

        return CompletableFuture.completedFuture(retureInfo);
    }


    private int[] findMinMaxXY(Rectangle rectangle, VisionIntentSquareAggreGation squareAggreGation) {
        int closeMinCenterX = findClosest(squareAggreGation.getCenterXList(), rectangle.getLeftTopX());
        int closeMinCenterY = findClosest(squareAggreGation.getCenterYList(), rectangle.getLeftTopY());
        int closeMaxCenterX = findClosest(squareAggreGation.getCenterXList(), rectangle.getLeftTopX() + rectangle.getWidth());
        int closeMaxCenterY = findClosest(squareAggreGation.getCenterYList(), rectangle.getLeftTopY() + rectangle.getHeight());

        int beginCenterX, beginCenterY, endCenterX, endCenterY;
        if (squareAggreGation.getCenterXList().get(closeMinCenterX).doubleValue() < rectangle.getLeftTopX()) {
            beginCenterX = closeMinCenterX + 1;
        } else {
            beginCenterX = closeMinCenterX;
        }
        if (squareAggreGation.getCenterYList().get(closeMinCenterY).doubleValue() < rectangle.getLeftTopY()) {
            beginCenterY = closeMinCenterY + 1;
        } else {
            beginCenterY = closeMinCenterY;
        }
        if (squareAggreGation.getCenterXList().get(closeMaxCenterX).doubleValue() < rectangle.getLeftTopX() + rectangle.getWidth()) {
            endCenterX = closeMaxCenterX;
        } else {
            endCenterX = closeMaxCenterX - 1;
        }
        if (squareAggreGation.getCenterYList().get(closeMaxCenterY).doubleValue() < rectangle.getLeftTopY() + rectangle.getHeight()) {
            endCenterY = closeMaxCenterY;
        } else {
            endCenterY = closeMaxCenterY - 1;
        }
        int[] result = new int[4];
        result[0] = beginCenterX;
        result[1] = beginCenterY;
        result[2] = endCenterX;
        result[3] = endCenterY;

        return result;
    }

    private int findClosest(List<BigDecimal> bigList, double target) {
        if (bigList.isEmpty()) return 0;

        int result = 0;
        double minDistance = Integer.MAX_VALUE;

        for (int i = 0; i < bigList.size(); i++) {
            double distance = Math.abs(bigList.get(i).doubleValue() - target);
            if (distance < minDistance) {
                minDistance = distance;
                result = i;
            }
        }

        return result;
    }

    private static double twoPointLen(double x1, double y1, double x2, double y2) {
        BigDecimal xDistance = new BigDecimal(x1).subtract(new BigDecimal(x2)).abs();
        BigDecimal yDistance = new BigDecimal(y1).subtract(new BigDecimal(y2)).abs();

        BigDecimal temp = xDistance.multiply(xDistance).add(yDistance.multiply(yDistance));

        double distance = Math.sqrt(temp.doubleValue());

        return distance;
    }

    /**
     * 根据距离或者正方形的视觉强度等级
     *
     * @param distance
     * @param rList
     * @return
     */
    private static int getGrade(double distance, BigDecimal[] rList) {
        if (distance <= rList[0].doubleValue()) {
            return VisionIntentSquare.Vision_Grade_First;
        } else if (distance <= rList[1].doubleValue()) {
            return VisionIntentSquare.Vision_Grade_Second;
        } else if (distance <= rList[2].doubleValue()) {
            return VisionIntentSquare.Vision_Grade_Third;
        } else if (distance <= rList[3].doubleValue()) {
            return VisionIntentSquare.Vision_Grade_Forth;
        } else if (distance <= rList[4].doubleValue()) {
            return VisionIntentSquare.Vision_Grade_Fifth;
        } else {
            return VisionIntentSquare.Vision_Grade_Sixth;
        }
    }

}
